package ar.uba.is2.common.mensaje;

import java.io.StringReader;
import java.util.Date;
import java.util.Scanner;
import java.util.regex.Pattern;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

/**
 * Mensaje. La identificacion univoca de un
 * mensaje esta dada por origen + fechaCreacion + idMensaje + parte.
 */
public class Mensaje {
	
	private static final Pattern DEFAULT_DELIMITER = Pattern.compile(",");
	
	private static final Pattern ALL_PATTERN = Pattern.compile(".+");
	
	public static Mensaje deserializar(String stream) {
		
		Mensaje msj = new Mensaje();
		
		if ( !(stream.startsWith("<") && stream.endsWith(">")) ) {
			return null;
		}

		stream = stream.substring(1, stream.length() - 1);
		Scanner scanner = new Scanner(new StringReader(stream));
		
		scanner.useDelimiter(DEFAULT_DELIMITER);
		
		int numeroMensaje = scanner.nextInt();
		msj.setNumeroMensaje(numeroMensaje);
		
		int cantidadPartes = scanner.nextInt();
		msj.setCantidadPartes(cantidadPartes);
		
		int parte = scanner.nextInt();
		msj.setParte(parte);
		
		long fechaCreacion = scanner.nextLong();
		msj.setFechaCreacion(new Date(fechaCreacion));
		
		String nombreOrigen = scanner.next();
		msj.setOrigen(new Componente(nombreOrigen));

		String nombreDestino = scanner.next();
		msj.setDestino(new Componente(nombreDestino));
		
		String tMensajeId = scanner.next();

		TipoMensaje tipoMensaje = TipoMensaje.deserializar(tMensajeId);
		msj.setTipoMensaje(tipoMensaje);
		
		String content = scanner.findInLine(ALL_PATTERN);
		
		Contenido contenido = tipoMensaje.instanciarContenido();
		contenido.deserializar(content.substring(1));
		msj.contenido = contenido;

		return msj;
	}

	/**
	 * Identifica un mensaje. Si un mensaje debe ser particionado, todas las
	 * particiones de un mismo mensaje coincidiran en este dato.
	 */
	private int numeroMensaje;

	/**
	 * Diferencia los mensajes de datos de los mensajes de control.
	 * 
	 */
	private TipoMensaje tipoMensaje;

	/**
	 * Cantidad de mensajes que se usan para enviar los datos.
	 */
	private int cantidadPartes = 1;

	/**
	 * El mesaje puede ser particionado para que pueda ser enviado por la red.
	 * Parte identifica el segmento del mensaje
	 */
	private int parte = 0;

	/**
	 * Fecha en la que se crea el mensaje. Un mensaje comparte la misma fecha en
	 * todas sus partes.
	 */
	private Date fechaCreacion;

	/**
	 * Emisor del mensaje.
	 */
	private Componente origen;

	/**
	 * Receptor del mensaje.
	 */
	private Componente destino;

	/**
	 * Contenido del mensaje (depende del tipo de mensaje)
	 */
	private Contenido contenido;

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof Mensaje)) {
			return false;
		}
		Mensaje that = (Mensaje) obj;

		return new EqualsBuilder().append(this.numeroMensaje,
				that.numeroMensaje).append(this.tipoMensaje, that.tipoMensaje)
				.append(this.cantidadPartes, that.cantidadPartes).append(
						this.parte, that.parte)
				.append(this.origen, that.origen).append(this.destino,
						that.destino).append(this.fechaCreacion,
						that.fechaCreacion).append(this.contenido,
						that.contenido).isEquals();
	}
	
	public int getCantidadPartes() {
		return cantidadPartes;
	}

	public Contenido getContenido() {
		return this.contenido;
	}

	public Componente getDestino() {
		return destino;
	}

	public Date getFechaCreacion() {
		return fechaCreacion;
	}

	public int getNumeroMensaje() {
		return numeroMensaje;
	}

	public Componente getOrigen() {
		return origen;
	}

	public int getParte() {
		return parte;
	}

	public TipoMensaje getTipoMensaje() {
		return tipoMensaje;
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder().append(this.numeroMensaje).append(
				this.tipoMensaje).append(this.cantidadPartes)
				.append(this.parte).append(this.origen).append(this.destino)
				.append(this.fechaCreacion).append(this.contenido).toHashCode();
	}

	public String serializar() {

		StringBuilder sb = new StringBuilder();

		sb.append("<").append(this.numeroMensaje).append(",").append(
				this.cantidadPartes).append(",").append(this.parte).append(",")
				.append(this.fechaCreacion.getTime()).append(",").append(
						this.origen.getNombre()).append(",").append(
						this.destino.getNombre()).append(",").append(
						this.tipoMensaje.serializar()).append(",").append(
						this.contenido.serializar()).append(">");

		return sb.toString();
	}

	public void setCantidadPartes(int cantidadPartes) {
		this.cantidadPartes = cantidadPartes;
	}

	public void setContenido(Contenido contenido) {
		this.contenido = contenido;
	}

	public void setDestino(Componente destino) {
		this.destino = destino;
	}

	public void setFechaCreacion(Date fechaCreacion) {
		this.fechaCreacion = fechaCreacion;
	}

	public void setNumeroMensaje(int numeroMensaje) {
		this.numeroMensaje = numeroMensaje;
	}

	public void setOrigen(Componente origen) {
		this.origen = origen;
	}

	public void setParte(int parte) {
		this.parte = parte;
	}

	public void setTipoMensaje(TipoMensaje tipoMensaje) {
		this.tipoMensaje = tipoMensaje;
	}

	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE);
	}
}
